﻿using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Management.Instrumentation;
using SkaldRogueLike.Main.CellContent;
using SkaldRogueLike.Main.CellContent.ContentFactories;
using SkaldRogueLike.Main.CellContent.Creatures;
using SkaldRogueLike.Main.CellContent.Items;
using SkaldRogueLike.Main.Common;
using SkaldRogueLike.Main.Constants.Enumerations;
using SkaldRogueLike.Main.DungeonGenerating;
using SkaldRogueLike.Main.DungeonGenerating.MapImplementations;

namespace CatSkaldRoguelike.ConsoleGame
{
    public class GameCreator
    {
        private readonly Dungeon _dungeon;

        public Tile[,] Map { get; private set; }

        public List<Creature> Creatures { get; private set; }
        public List<Item> Items { get; private set; }

        public Character Character { get; private set; }

        public GameCreator(Dungeon dungeon)
        {
            _dungeon = dungeon;
            Map = MapConverter.ConvertDungeonToTiles(_dungeon);

            Creatures = new List<Creature>();
            Items = new List<Item>();

            Character = CreateCharacter();
        }

        private Character CreateCharacter()
        {
            for (var x = 0; x < Map.GetLength(0); x++)
                for (var y = 0; y < Map.GetLength(1); y++)
                    if (Map[x, y] == Tile.DownStairs)
                    {
                        var result = new Character("John Smith");
                        result.Move(x, y);
                        return result;
                    }
            throw new InstanceNotFoundException("Upstairs");
        }

        #region CellContent

        public IEnumerable<Point> GetFreeCells()
        {
            for (var x = 0; x < Map.GetLength(0); x++)
                for (var y = 0; y < Map.GetLength(1); y++)
                    if (Map[x, y] == Tile.Empty)
                        yield return new Point(x, y);
        }

        public void CreateCellContent(int count, IGameFactory factory)
        {
            var result = new List<CellContent>(count);
            while (count > 0)
            {
                result.Add(factory.Create());
                count--;
            }
            if (factory is ItemFactory)
                Items.AddRange(result.OfType<Item>());
            else if (factory is MonsterFactory)
                Creatures.AddRange(result.OfType<Creature>());
        }

        public void FillMapWithCellContent()
        {
            var freeCells = GetFreeCells().ToList();
            var cellsWithMonsters = new List<Point>(Creatures.Count);
            Point current;
            foreach (var creature in Creatures.Where(it => it.HP > 0).Take(freeCells.Count))
            {
                current = new Point(creature.X, creature.Y);
                if ((current.X == 0 || current.Y == 0) && !freeCells.Contains(current))
                    current = freeCells[StaticRandom.Next(freeCells.Count)];
                while (cellsWithMonsters.Contains(current))
                    current = freeCells[StaticRandom.Next(freeCells.Count)];
                creature.Move(current.X, current.Y);
                cellsWithMonsters.Add(current);
            }
            var itemsInBag = new List<Item>(Items.Count);
            foreach (var item in Items)
            {
                if (item.X != 0 && item.Y != 0 && freeCells.Contains(new Point(item.X, item.Y)))
                    current = new Point(item.X, item.Y);
                else
                    current = freeCells[StaticRandom.Next(freeCells.Count)];
                if (cellsWithMonsters.Contains(current))
                {
                    Creatures.First(it => it.X == current.X && it.Y == current.Y).PickUp(item);
                    itemsInBag.Add(item);
                }
                else
                    item.Move(current.X, current.Y);
            }
            Items.RemoveAll(itemsInBag.Contains);
        }

        #endregion
        
        public Cell[,] GenerateFilledMapPicture()
        {
            var result = new Cell[Map.GetLength(0), Map.GetLength(1)];
            for (var x = 0; x < Map.GetLength(0); x++)
                for (var y = 0; y < Map.GetLength(1); y++)
                    result[x, y] = new Cell(x, y,
                        ((char)Map[x, y]).ToString(CultureInfo.InvariantCulture),
                        Map[x, y] == Tile.Door ? Colors.DarkYellow : Colors.DarkGray
                        );
            return result;
        }
    }
}